#pragma once

#include "factory.h"

#include <vector>

template < class CONCRETE_ACTOR, class REPLICA >
class Actor;

template < class CONCRETE_ACTOR, class REPLICA >
class Message
{
	typedef typename ActorsFactory < CONCRETE_ACTOR, REPLICA >::Id Id;
public:
	virtual ~Message ()
	{
	}
	virtual void Execute ( CONCRETE_ACTOR & actor ) = 0;
protected:
	Message ()
	{
	}
	static void SendNewMessage ( const Id & actorId, Message < CONCRETE_ACTOR, REPLICA > * message )
	{
		Actor < CONCRETE_ACTOR, REPLICA > * actor = actorId.GetActor ();
		if ( actor )
			actor->ReceiveMessage ( message );
	}
};

template < class CONCRETE_ACTOR, class REPLICA >
class Actor
{
	friend class Message < CONCRETE_ACTOR, REPLICA >;
	friend class ActorsFactory < CONCRETE_ACTOR, REPLICA >;
public:
	typedef typename ActorsFactory < CONCRETE_ACTOR, REPLICA >::Id Id;
	Actor ()
	{
	}
	virtual ~Actor ()
	{
		for ( std::size_t i = 0; i < messages.size (); i++ )
			delete messages.at ( i );
		messages.clear ();
	}
	const Id & GetId () const
	{
		return id;
	}
	const REPLICA & GetReplica () const
	{
		return replica;
	}
protected:
	virtual void UpdateReplica ( REPLICA & replica ) const = 0;
private:
	void SetId ( const Id & newId )
	{
		id = newId;
	}
	void ReceiveMessage ( Message < CONCRETE_ACTOR, REPLICA > * message )
	{
		messages.push_back ( message );
	}
	void HandleMessages ()
	{
		MessagesCollection messagesCopy = messages;
		messages.clear ();
		for ( std::size_t i = 0; i < messagesCopy.size (); i++ )
		{
			messagesCopy.at ( i )->Execute ( static_cast < CONCRETE_ACTOR & > ( * this ) );
			delete messagesCopy.at ( i );
		}
	}
	void DoUpdateReplica ()
	{
		UpdateReplica ( replica );
	}
private:
	typedef std::vector < Message < CONCRETE_ACTOR, REPLICA > * > MessagesCollection;
	MessagesCollection messages;
	Id id;
	REPLICA replica;
};
